package com.wiscamp.ninechapters.challenges.domain.repositories;

import com.wiscamp.ninechapters.challenges.domain.models.UserPoints;
import com.wiscamp.ninechapters.challenges.domain.models.UserPointsMonth;
import com.wiscamp.ninechapters.challenges.domain.models.UserPointsWeek;
import com.wiscamp.ninechapters.challenges.infrastructure.mappers.UserPointsMapper;
import com.wiscamp.ninechapters.challenges.infrastructure.mappers.UserPointsMonthMapper;
import com.wiscamp.ninechapters.challenges.infrastructure.mappers.UserPointsWeekMapper;
import com.wiscamp.ninechapters.challenges.infrastructure.po.UserPointsMonthPO;
import com.wiscamp.ninechapters.challenges.infrastructure.po.UserPointsPO;
import com.wiscamp.ninechapters.challenges.infrastructure.po.UserPointsWeekPO;
import com.wiscamp.ninechapters.common.core.CollectionHelper;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Repository;

import java.util.Collection;
import java.util.Objects;

@Repository
public class PointsRepository {
    @Autowired
    private UserPointsMonthMapper userPointsMonthMapper;

    @Autowired
    private UserPointsWeekMapper userPointsWeekMapper;

    @Autowired
    private UserPointsMapper userPointsMapper;

    //region converter
    public UserPoints toUserPoints(@NotNull UserPointsPO po) {
        var entity = new UserPoints();
        BeanUtils.copyProperties(po, entity);
        return entity;
    }

    public UserPointsPO toUserPointsPO(@NotNull UserPoints entity) {
        var po = new UserPointsPO();
        BeanUtils.copyProperties(entity, po);
        return po;
    }

    public UserPointsWeek toUserPointsWeek(@NotNull UserPointsWeekPO po) {
        var entity = new UserPointsWeek();
        BeanUtils.copyProperties(po, entity);
        return entity;
    }

    public UserPointsWeekPO toUserPointsWeekPO(@NotNull UserPointsWeek entity) {
        var po = new UserPointsWeekPO();
        BeanUtils.copyProperties(entity, po);
        return po;
    }

    public UserPointsMonth toUserPointsMonth(@NotNull UserPointsMonthPO po) {
        var entity = new UserPointsMonth();
        BeanUtils.copyProperties(po, entity);
        return entity;
    }

    public UserPointsMonthPO toUserPointsMonthPO(@NotNull UserPointsMonth entity) {
        var po = new UserPointsMonthPO();
        BeanUtils.copyProperties(entity, po);
        return po;
    }
    //endregion

    public UserPoints getUserPointsByUser(long userId) {
        var po = userPointsMapper.findByUserId(userId);
        if (Objects.nonNull(po))
            return toUserPoints(po);
        return null;
    }

    public UserPointsWeek getUserPointsWeekByUser(long userId, int year, int weekOfYear) {
        var po = userPointsWeekMapper.findByUserIdAndDateYearAndWeekOfYear(userId, year, weekOfYear);
        if (Objects.nonNull(po))
            return toUserPointsWeek(po);
        return null;
    }

    public UserPointsMonth getUserPointsMonthByUser(long userId, int year, int month) {
        var po = userPointsMonthMapper.findByUserIdAndDateYearAndDateMonth(userId, year, month);
        if (Objects.nonNull(po))
            return toUserPointsMonth(po);
        return null;
    }

    public UserPoints saveUserPoints(UserPoints userPoints) {
        var po = toUserPointsPO(userPoints);
        var savedPO = userPointsMapper.saveAndFlush(po);
        if (Objects.nonNull(savedPO))
            return toUserPoints(savedPO);
        return null;
    }

    public Collection<UserPoints> saveUserPointsList(Collection<UserPoints> userPointsList) {
        var poList = CollectionHelper.convertToArrayList(userPointsList, this::toUserPointsPO);
        var savedPOList = userPointsMapper.saveAll(poList);
        return CollectionHelper.convertToArrayList(savedPOList, this::toUserPoints);
    }

    public UserPointsWeek saveUserPointsWeek(UserPointsWeek userPointsWeek) {
        var po = toUserPointsWeekPO(userPointsWeek);
        var savedPO = userPointsWeekMapper.saveAndFlush(po);
        if (Objects.nonNull(savedPO))
            return toUserPointsWeek(savedPO);
        return null;
    }

    public Collection<UserPointsWeek> saveUserPointsWeekList(Collection<UserPointsWeek> userPointsWeekList) {
        var poList = CollectionHelper.convertToArrayList(userPointsWeekList, this::toUserPointsWeekPO);
        var savedPOList = userPointsWeekMapper.saveAll(poList);
        return CollectionHelper.convertToArrayList(savedPOList, this::toUserPointsWeek);
    }

    public UserPointsMonth saveUserPointsMonth(UserPointsMonth userPointsMonth) {
        var po = toUserPointsMonthPO(userPointsMonth);
        var savedPO = userPointsMonthMapper.saveAndFlush(po);
        if (Objects.nonNull(savedPO))
            return toUserPointsMonth(savedPO);
        return null;
    }

    public Collection<UserPointsMonth> saveUserPointsMonthList(Collection<UserPointsMonth> userPointsMonthList) {
        var poList = CollectionHelper.convertToArrayList(userPointsMonthList, this::toUserPointsMonthPO);
        var savedPOList = userPointsMonthMapper.saveAll(poList);
        return CollectionHelper.convertToArrayList(savedPOList, this::toUserPointsMonth);
    }

    public Collection<UserPoints> getTop100OrderByPoints(PageRequest pageable) {
        var pageableUserPoints = userPointsMapper.getTop100OrderByPoints(pageable);
        var userPointsPoList = pageableUserPoints.getContent();
        return CollectionHelper.convertToArrayList(userPointsPoList, this::toUserPoints);
    }

    public void rankingAfter100ByPoints(Collection<Long> userIds) {
        userPointsMapper.rankingAfter100ByPoints(userIds);
    }

    public Collection<UserPointsMonth> getTop100OrderByMonthPoints(int year, int monthValue, PageRequest pageable) {
        var pageableList = userPointsMonthMapper.getTop100OrderByMonthPoints(year, monthValue, pageable);
        var userPointsMonthList = pageableList.getContent();
        return CollectionHelper.convertToArrayList(userPointsMonthList, this::toUserPointsMonth);
    }

    public Collection<UserPointsWeek> getTop100OrderByWeekPoints(int year, int week, PageRequest pageable) {
        var pageableList = userPointsWeekMapper.getTop100OrderByWeekPoints(year, week, pageable);
        var userPointsWeekList = pageableList.getContent();
        return CollectionHelper.convertToArrayList(userPointsWeekList, this::toUserPointsWeek);
    }
}
